package service

import (
	"context"
	"database/sql"
	"encoding/json"
	"fmt"
	"github.com/gogo/protobuf/types"
	"github.com/pkg/errors"
	"gorm.io/gorm"
	"inventory/api/inventory"
	"inventory/api/prod"
	"inventory/internal/constant"
	"inventory/internal/model"
	"inventory/pkg/ecode"
	"inventory/pkg/util"
	"strconv"
	"strings"
	"time"
)

func formatStorageUnavailableError(storage *model.Storage) error {
	remark := model.StorageRealTimeRemark{}
	if err := json.Unmarshal(storage.Remark, &remark); err != nil {
		return err
	}
	return ecode.AppendMessage(constant.ErrStorageUnavailable,
		fmt.Sprintf("商品%s库存不足", remark.Name))
}

// GetOrderStorageStatusMap 获取订单库存分布
func (s *Service) GetOrderStorageStatusMap(ctx context.Context, processOrders []*model.ProcessOrder, sgID uint32) (
	orderStorageMap map[string]int, err error) {
	var (
		orderIDs       []uint64
		processDetails []*model.ProcessDetail
		warehouses     []*model.Warehouse
	)
	warehouseKeyMap := make(util.UInt32BoolMap)
	warehouseMap := make(map[uint32]*model.Warehouse)
	for _, o := range processOrders {
		orderIDs = append(orderIDs, o.ID)
		warehouseKeyMap[o.WarehouseID] = true
	}
	if warehouses, err = s.dao.FindWarehousesByIDs(ctx, warehouseKeyMap.GetKeys(), sgID); err != nil {
		return
	}
	for _, w := range warehouses {
		warehouseMap[w.ID] = w
	}
	if processDetails, err = s.dao.FindProcessOrderDetailsByOrderIDs(ctx, orderIDs); err != nil {
		return
	}
	orderDetailsRef := make(map[uint64]map[string]float64)
	storageKeyMap := make(map[uint32]util.StringBoolMap)
	for _, d := range processDetails {
		if d.IsAccounting {
			if _, ok := orderDetailsRef[d.OrderID]; !ok {
				orderDetailsRef[d.OrderID] = make(map[string]float64)
			}
			orderDetailsRef[d.OrderID][d.StorageKey] += d.Quantity
			if _, ok := storageKeyMap[d.WarehouseID]; !ok {
				storageKeyMap[d.WarehouseID] = make(util.StringBoolMap)
			}
			storageKeyMap[d.WarehouseID][d.StorageKey] = true
		}
	}
	storageMap := make(map[uint32]model.StorageMap)
	for warehouseID, subKeyMap := range storageKeyMap {
		var storages []*model.Storage
		if storages, err = s.dao.FindWarehouseStoragesByStorageKeys(ctx, warehouseID, subKeyMap.GetKeys()); err != nil {
			return
		}
		subStorageMap := make(model.StorageMap)
		subStorageMap.InitStorageMap(storages)
		storageMap[warehouseID] = subStorageMap
	}
	orderStorageMap = make(map[string]int)
	for _, o := range processOrders {
		var storageStatus int // 库存状态，0~充足，1~不足
		warehouse := warehouseMap[o.WarehouseID]
		for k, q := range orderDetailsRef[o.ID] {
			storage, ok := storageMap[o.WarehouseID][k]
			// 不存在storageKey
			if !ok {
				// 强管控展示缺货
				if warehouse.StorageMode == constant.WarehouseStorageModeStrong {
					storageStatus = 1
					break
				}
				// 弱管控在无明确库存记录时视为无限量
				continue
			} else if storage.Quantity-q < 0 {
				// 有明确库存记录时，不区分管控模式，按照实际展示缺货标识
				storageStatus = 1
				break
			}
		}
		orderStorageMap[o.OrderNumber] = storageStatus
	}
	return
}

// 获取指定仓库商品库存信息
func (s *Service) GetWarehouseProdSkus(ctx context.Context, req *inventory.GetWarehouseProdSkusRequest) (
	resp *inventory.GetWarehouseProdSkusResponse, err error) {
	var (
		warehouse *model.Warehouse
		boms      []*model.BomConfig
	)
	if warehouse, err = s.dao.GetWarehouseInfoInternal(ctx, req.Id); err != nil {
		return
	}
	bomMap := make(map[string]map[string]float64)
	materialKeyMap := make(util.StringBoolMap)
	if boms, err = s.dao.FindBomByRefIds(ctx, warehouse.SgID, req.ProdIds); err != nil {
		return
	}
	for _, bom := range boms {
		if _, ok := bomMap[bom.StorageKey]; !ok {
			bomMap[bom.StorageKey] = make(map[string]float64)
		}
		materialKey := fmt.Sprintf("m-%d", bom.MaterialID)
		bomMap[bom.StorageKey][materialKey] = bom.Quantity
		materialKeyMap[materialKey] = true
	}
	storageMap := make(model.StorageMap)
	var storages []*model.Storage
	if storages, err = s.dao.FindWarehouseStoragesByProductIDs(ctx, warehouse.ID, req.ProdIds); err != nil {
		return
	}
	storageMap.InitStorageMap(storages)
	if len(materialKeyMap) != 0 {
		if storages, err = s.dao.FindWarehouseStoragesByStorageKeys(ctx, warehouse.ID, materialKeyMap.GetKeys()); err != nil {
			return
		}
		storageMap.InitStorageMap(storages)
	}
	prodStorageMap := make(map[string]int)
	for rawStorageKey, storage := range storageMap {
		if inventory.StorageType(storage.StorageType) == inventory.StorageType_PROD {
			if _, hasBomMap := bomMap[rawStorageKey]; !hasBomMap {
				expectQuantity := int(storage.Quantity - storage.FreezeQuantity)
				if expectQuantity < 0 {
					expectQuantity = 0
				}
				prodStorageMap[rawStorageKey] = expectQuantity
			}
		}
	}
	for bomStorageKey, materialMap := range bomMap {
		for materialKey, refQuantity := range materialMap {
			if materialStorage, hasMaterialStorage := storageMap[materialKey]; hasMaterialStorage {
				expectQuantity := int((materialStorage.Quantity - materialStorage.FreezeQuantity) / refQuantity)
				if expectQuantity < 0 {
					expectQuantity = 0
				}
				if _, ok := prodStorageMap[bomStorageKey]; !ok || expectQuantity < prodStorageMap[bomStorageKey] {
					prodStorageMap[bomStorageKey] = expectQuantity
				}
			} else if warehouse.StorageMode == constant.WarehouseStorageModeStrong {
				prodStorageMap[bomStorageKey] = 0
			}
		}
	}
	prodViews := make([]*inventory.WarehouseProdSku, 0)
	for storageKey, intStorage := range prodStorageMap {
		keys := strings.Split(storageKey, "-")
		prodID, _ := strconv.Atoi(keys[0])
		skuID, _ := strconv.Atoi(keys[1])
		prodViews = append(prodViews, &inventory.WarehouseProdSku{
			ProdId:  uint64(prodID),
			SkuId:   uint64(skuID),
			Storage: int64(intStorage),
		})
	}
	resp = &inventory.GetWarehouseProdSkusResponse{Prods: prodViews}
	return
}

// GetOrderInfoStorage 查看履约单详情明细库存+通知
func (s *Service) GetOrderInfoStorage(ctx context.Context, req *inventory.GetOrderInfoStorageRequest) (resp *inventory.GetOrderInfoStorageResponse, err error) {
	var (
		processOrder   *model.ProcessOrder
		processDetails []*model.ProcessDetail
		mainRef        []*model.ProcessDetail
		warehouse      *model.Warehouse
		storages       []*model.Storage
	)
	if processOrder, err = s.dao.GetProcessOrder(ctx, req.OrderNumber); err != nil {
		if errors.Cause(err) == gorm.ErrRecordNotFound {
			err = constant.ErrOrderNotExist
		}
		return
	}
	if warehouse, err = s.dao.GetWarehouseInfoInternal(ctx, processOrder.WarehouseID); err != nil {
		return
	}
	if processDetails, err = s.dao.FindProcessOrderDetailsByOrderIDs(ctx, []uint64{processOrder.ID}); err != nil {
		return
	}
	storageKeyMap := make(util.StringBoolMap)
	subRef := make(map[uint64][]*model.ProcessDetail)
	for _, d := range processDetails {
		if d.IsAccounting {
			storageKeyMap[d.StorageKey] = true
		}
		if d.ParentID == 0 {
			mainRef = append(mainRef, d)
			subRef[d.ID] = []*model.ProcessDetail{}
		} else {
			subRef[d.ParentID] = append(subRef[d.ParentID], d)
		}
	}
	storageMap := make(model.StorageMap)
	if !processOrder.DeliverTime.Valid {
		// 未发货时需要加载库存信息
		if storages, err = s.dao.FindWarehouseStoragesByStorageKeys(ctx, warehouse.ID, storageKeyMap.GetKeys()); err != nil {
			return
		}
		storageMap.InitStorageMap(storages)
	}
	respDetails := make([]*inventory.DetailStorageView, 0)
	for _, d := range mainRef {
		var (
			storageStatus   int // 0~充足，1~不足（未通知），2~不足（已通知）
			storageQuantity sql.NullFloat64
		)
		if subs, hasSub := subRef[d.ID]; !hasSub || len(subs) == 0 {
			// 无子明细
			// 这里履约单如果创建成功 = 冻结成功 = 即强管控模式时必定有storage记录，无需判定模式；无记录一定为弱管控，可以视为无限量
			if storage, hasStorage := storageMap[d.StorageKey]; hasStorage {
				// 无需处理冻结库存，这里都是冻结过的
				_ = storageQuantity.Scan(storage.Quantity)
			}
		} else {
			for _, sub := range subs {
				if storage, hasStorage := storageMap[sub.StorageKey]; hasStorage {
					// 无需处理冻结库存，这里都是冻结过的
					validQuantity := storage.Quantity * d.Quantity / sub.Quantity
					if !storageQuantity.Valid || storageQuantity.Float64 > validQuantity {
						// 未初始化 || 子物料相对库存小于对象库存时，更新为较小的
						_ = storageQuantity.Scan(validQuantity)
					}
				}
			}

		}
		if storageQuantity.Valid {
			if d.NotifyFlag {
				storageStatus = 2
			} else {
				storageStatus = 1
			}
		}
		respDetails = append(respDetails, &inventory.DetailStorageView{
			StorageKey:    d.StorageKey,
			StorageStatus: uint32(storageStatus),
		})
	}
	resp = &inventory.GetOrderInfoStorageResponse{
		Details: respDetails,
	}
	return
}

// GetProdStorages 查看商品库存分布
func (s *Service) GetProdStorages(ctx context.Context, req *inventory.GetProdStoragesRequest) (resp *inventory.GetProdStoragesResponse, err error) {
	var (
		storages    []*model.Storage
		boms        []*model.BomConfig
		materialIds []uint64
		warehouses  []*model.Warehouse
	)
	sgId := req.WebToken.SgId
	prodIds := make([]uint64, len(req.ProdIds))
	for index, prodID := range req.ProdIds {
		prodIds[index] = prodID
	}
	bomMap := make(map[uint64]map[string]map[string]float64)
	if boms, err = s.dao.FindBomByRefIds(ctx, sgId, prodIds); err != nil {
		return
	}
	for _, bom := range boms {
		if _, ok := bomMap[bom.RefID]; !ok {
			bomMap[bom.RefID] = make(map[string]map[string]float64)
		}
		if _, ok := bomMap[bom.RefID][bom.StorageKey]; !ok {
			bomMap[bom.RefID][bom.StorageKey] = make(map[string]float64)
		}
		materialKey := fmt.Sprintf("m-%d", bom.MaterialID)
		bomMap[bom.RefID][bom.StorageKey][materialKey] = bom.Quantity
		materialIds = append(materialIds, bom.MaterialID)
	}
	if storages, err = s.dao.FindStoragesByProductIDs(ctx, req.ProdIds); err != nil {
		return
	}
	warehouseIDMap := make(util.UInt32BoolMap)
	productQuantities := make(map[uint64]int)
	productUnit := make(map[uint64]string)
	productWarehouses := make(map[uint64]util.UInt32BoolMap)
	for _, v := range storages {
		warehouseIDMap[v.WarehouseID] = true
		if _, ok := productUnit[*v.ProdID]; !ok {
			r := struct {
				Unit string `json:"unit"`
			}{}
			if err = json.Unmarshal(v.Remark, &r); err != nil {
				return
			}
			productUnit[*v.ProdID] = r.Unit
			productQuantities[*v.ProdID] = 0
			productWarehouses[*v.ProdID] = make(util.UInt32BoolMap)
		}
		productQuantities[*v.ProdID] += int(v.Quantity - v.FreezeQuantity)
		productWarehouses[*v.ProdID][v.WarehouseID] = true
	}
	if len(materialIds) != 0 {
		// 基于bom侧补全bom商品
		materialStorageMap := make(map[uint32]model.StorageMap)
		if storages, err = s.dao.FindStoragesByMaterialIDs(ctx, materialIds); err != nil {
			return
		}
		for _, storage := range storages {
			if _, ok := materialStorageMap[storage.WarehouseID]; !ok {
				materialStorageMap[storage.WarehouseID] = make(model.StorageMap)
			}
			materialStorageMap[storage.WarehouseID][storage.StorageKey] = storage
		}
		for warehouseID, storageMap := range materialStorageMap {
			warehouseIDMap[warehouseID] = true
			for prodId, sMap := range bomMap {
				if _, ok := productUnit[prodId]; !ok {
					// 这里必须与商城一起改造，从metadata读取token才能从商品中心加载商品单位
					productUnit[prodId] = ""
					productQuantities[prodId] = 0
					productWarehouses[prodId] = make(util.UInt32BoolMap)
				}
				for storageKey, mMap := range sMap {
					hasAllMaterial := true
					expectMap := make(map[string]int)
					for materialKey, refQuantity := range mMap {
						if storage, ok := storageMap[materialKey]; ok {
							expectQuantity := int((storage.Quantity - storage.FreezeQuantity) / refQuantity)
							if expectQuantity < 0 {
								expectQuantity = 0
							}
							if lastQuantity, hasQuantity := expectMap[storageKey]; !hasQuantity || lastQuantity > expectQuantity {
								expectMap[storageKey] = expectQuantity
							}
						} else {
							hasAllMaterial = false
							break
						}
					}
					if hasAllMaterial {
						productQuantities[prodId] += expectMap[storageKey]
						productWarehouses[prodId][warehouseID] = true
					}
				}
			}
		}
	}
	warehouseMap := make(model.WarehouseMap)
	if len(warehouseIDMap) != 0 {
		if warehouses, err = s.dao.FindWarehousesByIDs(ctx, warehouseIDMap.GetKeys(), sgId); err != nil {
			return
		}
		for _, w := range warehouses {
			warehouseMap[w.ID] = w
		}
	}
	views := make([]*inventory.ProdStorageView, 0, len(prodIds))
	for _, prodID := range prodIds {
		if wMap, ok := productWarehouses[prodID]; ok {
			warehouseNames := make([]string, 0, len(wMap))
			for _, warehouseId := range wMap.GetKeys() {
				warehouseNames = append(warehouseNames, warehouseMap[warehouseId].Name)
			}
			views = append(views, &inventory.ProdStorageView{
				Id:         prodID,
				Storage:    int64(productQuantities[prodID]),
				Unit:       productUnit[prodID],
				Warehouses: warehouseNames,
			})
		} else {
			views = append(views, &inventory.ProdStorageView{
				Id:         prodID,
				Storage:    0,
				Unit:       "",
				Warehouses: []string{},
			})
		}
	}
	resp = &inventory.GetProdStoragesResponse{Prods: views}
	return
}

// GetProductSkus 获取sku
func (s *Service) GetProductSkus(ctx context.Context, req *inventory.GetProductSkusRequest) (resp *inventory.GetProductSkusResponse, err error) {
	var (
		boms        []*model.BomConfig
		storages    []*model.Storage
		materialIds []uint64
	)
	// 这里empId不允许引用
	sgId := req.WebToken.SgId
	quantityMap := make(map[string]int)
	bomMap := make(map[string]map[string]float64)
	if boms, err = s.dao.FindBomByRefIds(ctx, sgId, []uint64{req.ProdId}); err != nil {
		return
	}
	for _, bom := range boms {
		if _, ok := bomMap[bom.StorageKey]; !ok {
			bomMap[bom.StorageKey] = make(map[string]float64)
		}
		materialKey := fmt.Sprintf("m-%d", bom.MaterialID)
		bomMap[bom.StorageKey][materialKey] = bom.Quantity
		materialIds = append(materialIds, bom.MaterialID)
	}
	if len(materialIds) != 0 {
		// 基于bom侧补全bom商品
		materialStorageMap := make(map[uint32]model.StorageMap)
		if storages, err = s.dao.FindStoragesByMaterialIDs(ctx, materialIds); err != nil {
			return
		}
		for _, storage := range storages {
			if _, ok := materialStorageMap[storage.WarehouseID]; !ok {
				materialStorageMap[storage.WarehouseID] = make(model.StorageMap)
			}
			materialStorageMap[storage.WarehouseID][storage.StorageKey] = storage
		}
		for _, storageMap := range materialStorageMap {
			for storageKey, mMap := range bomMap {
				hasAllMaterial := true
				expectMap := make(map[string]int)
				for materialKey, refQuantity := range mMap {
					if storage, ok := storageMap[materialKey]; ok {
						expectQuantity := int((storage.Quantity - storage.FreezeQuantity) / refQuantity)
						if expectQuantity < 0 {
							expectQuantity = 0
						}
						if lastQuantity, hasQuantity := expectMap[storageKey]; !hasQuantity || lastQuantity > expectQuantity {
							expectMap[storageKey] = expectQuantity
						}
					} else {
						hasAllMaterial = false
						break
					}
				}
				if hasAllMaterial {
					if _, ok := storageMap[storageKey]; !ok {
						quantityMap[storageKey] = expectMap[storageKey]
					} else {
						quantityMap[storageKey] += expectMap[storageKey]
					}
				}
			}
		}
	}
	if storages, err = s.dao.FindStoragesByProductIDs(ctx, []uint64{req.ProdId}); err != nil {
		return
	}
	for _, storage := range storages {
		if _, ok := bomMap[storage.StorageKey]; ok {
			continue
		}
		quantity := int(storage.Quantity - storage.FreezeQuantity)
		if quantity < 0 {
			quantity = 0
		}
		if _, ok := quantityMap[storage.StorageKey]; !ok {
			quantityMap[storage.StorageKey] = quantity
		} else {
			quantityMap[storage.StorageKey] += quantity
		}
	}
	views := make([]*inventory.SkuView, 0)
	for storageKey, quantity := range quantityMap {
		var skuID int
		if skuID, err = strconv.Atoi(strings.Split(storageKey, "-")[1]); err != nil {
			return
		}
		views = append(views, &inventory.SkuView{
			Id:      uint64(skuID),
			Storage: int64(quantity),
		})
	}
	resp = &inventory.GetProductSkusResponse{Skus: views}
	return
}

// GetMultipleProdSkus 查看后台指定多个仓库指定sku的库存
func (s *Service) GetMultipleProdSkus(ctx context.Context, req *inventory.GetMultipleProdSkusRequest) (
	resp *inventory.GetMultipleProdSkusReply, err error) {
	var (
		warehouses  []*model.Warehouse
		boms        []*model.BomConfig
		materialIDs []uint64
	)
	// 这里empId不允许引用
	sgId := req.WebToken.SgId
	warehouseIDMap := make(util.UInt32BoolMap)
	allStorageKeyMap := make(util.StringBoolMap)
	for _, p := range req.Prods {
		if _, ok := warehouseIDMap[p.Id]; !ok {
			warehouseIDMap[p.Id] = true
		}
		allStorageKeyMap[fmt.Sprintf("%d-%d", p.ProdId, p.SkuId)] = true
	}
	warehouseMap := make(map[uint32]*model.Warehouse)
	if warehouses, err = s.dao.FindWarehousesByIDs(ctx, warehouseIDMap.GetKeys(), sgId); err != nil {
		return
	}
	if len(warehouses) != len(warehouseIDMap) {
		err = constant.ErrWarehouseNotExist
		return
	}
	for _, warehouse := range warehouses {
		warehouseMap[warehouse.ID] = warehouse
	}
	bomMap := make(map[string]map[string]float64)
	if boms, err = s.dao.FindBomByStorageKeys(ctx, allStorageKeyMap.GetKeys()); err != nil {
		return
	}
	for _, bom := range boms {
		if _, ok := bomMap[bom.StorageKey]; !ok {
			bomMap[bom.StorageKey] = make(map[string]float64)

		}
		bomMap[bom.StorageKey][fmt.Sprintf("m-%d", bom.MaterialID)] = bom.Quantity
		materialIDs = append(materialIDs, bom.MaterialID)
	}
	warehouseStorageKeyMap := make(map[uint32]util.StringBoolMap)
	warehouseProdMap := make(map[uint32]util.StringBoolMap)
	for _, p := range req.Prods {
		warehouseId := p.Id
		if _, ok := warehouseStorageKeyMap[warehouseId]; !ok {
			warehouseStorageKeyMap[warehouseId] = make(util.StringBoolMap)
			warehouseProdMap[warehouseId] = make(util.StringBoolMap)
		}
		storageKey := fmt.Sprintf("%d-%d", p.ProdId, p.SkuId)
		warehouseProdMap[warehouseId][storageKey] = true
		if materialMap, ok := bomMap[storageKey]; ok {
			for materialKey := range materialMap {
				warehouseStorageKeyMap[warehouseId][materialKey] = true
			}
		} else {
			warehouseStorageKeyMap[warehouseId][storageKey] = true
		}
	}
	allStorageMap := make(map[uint32]map[string]int)
	storageMap := make(map[uint32]model.StorageMap)
	for warehouseID, storageKeyMap := range warehouseStorageKeyMap {
		var storages []*model.Storage
		warehouse := warehouseMap[warehouseID]
		allStorageMap[warehouseID] = make(map[string]int)
		if storages, err = s.dao.FindWarehouseStoragesByStorageKeys(ctx, warehouseID, storageKeyMap.GetKeys()); err != nil {
			return
		}
		storageMap[warehouseID] = make(model.StorageMap)
		storageMap[warehouseID].InitStorageMap(storages)
		for storageKey := range warehouseProdMap[warehouseID] {
			if storage, hasProdStorage := storageMap[warehouseID][storageKey]; hasProdStorage {
				expectQuantity := int(storage.Quantity - storage.FreezeQuantity)
				if expectQuantity < 0 {
					expectQuantity = 0
				}
				allStorageMap[warehouseID][storage.StorageKey] = expectQuantity
			} else if materialMap, hasBomMap := bomMap[storageKey]; hasBomMap {
				for materialKey, refQuantity := range materialMap {
					if materialStorage, hasMaterialStorage := storageMap[warehouseID][materialKey]; hasMaterialStorage {
						expectQuantity := int((materialStorage.Quantity - materialStorage.FreezeQuantity) / refQuantity)
						if expectQuantity < 0 {
							expectQuantity = 0
						}
						allStorageMap[warehouseID][materialKey] = expectQuantity
						if _, ok := allStorageMap[warehouseID][storageKey]; !ok || expectQuantity < allStorageMap[warehouseID][storageKey] {
							allStorageMap[warehouseID][storageKey] = expectQuantity
						}
					} else if warehouse.StorageMode == constant.WarehouseStorageModeStrong {
						allStorageMap[warehouseID][storageKey] = 0
						allStorageMap[warehouseID][materialKey] = 0
					}
				}
			} else if warehouse.StorageMode == constant.WarehouseStorageModeStrong {
				allStorageMap[warehouseID][storageKey] = 0
			}
		}
	}
	prodViews := make([]*inventory.MultipleProductResp, 0)
	for _, p := range req.Prods {
		warehouseId := p.Id
		storageKey := fmt.Sprintf("%d-%d", p.ProdId, p.SkuId)
		prodView := &inventory.MultipleProductResp{
			Id:     p.Id,
			ProdId: p.ProdId,
			SkuId:  p.SkuId,
		}
		if quantity, ok := allStorageMap[warehouseId][storageKey]; ok {
			// 这里目的是返回长度足够的int，pb的int64会转成string，故这里用double
			prodView.Storage = &types.DoubleValue{Value: float64(quantity)}
		}
		if req.Type == 1 && len(materialIDs) != 0 {
			var materialRemarks map[string]*model.FullRemark
			if materialRemarks, err = s.dao.BuildMaterialsRemarks(ctx, sgId, materialIDs); err != nil {
				return
			}
			if materialMap, ok := bomMap[storageKey]; ok {
				for materialKey, refQuantity := range materialMap {
					materialID, _ := strconv.Atoi(materialKey[2:])
					materialQuantity := allStorageMap[warehouseId][materialKey]
					prodView.Subs = append(prodView.Subs, &inventory.MultipleMaterialResp{
						Id:          uint32(materialID),
						Name:        materialRemarks[materialKey].Name,
						Unit:        materialRemarks[materialKey].Unit,
						Code:        materialRemarks[materialKey].Code,
						Storage:     &types.DoubleValue{Value: float64(materialQuantity)},
						RefQuantity: refQuantity,
					})
				}
			}
		}
		prodViews = append(prodViews, prodView)
	}
	resp = &inventory.GetMultipleProdSkusReply{Prods: prodViews}
	return
}

// GetOrderListStorage 查看履约单列表库存是否充足
func (s *Service) GetOrderListStorage(ctx context.Context, req *inventory.GetOrderListStorageRequest) (resp *inventory.GetOrderListStorageResponse, err error) {
	var (
		orderStatusMap map[string]int
		processOrders  []*model.ProcessOrder
	)
	orderList := make([]*inventory.OrderStorageView, 0)
	if processOrders, err = s.dao.FindProcessOrdersByOrderNumbers(ctx, req.OrderList); err != nil {
		return
	}
	if orderStatusMap, err = s.GetOrderStorageStatusMap(ctx, processOrders, req.SgId); err != nil {
		return
	}
	for orderNumber, storageStatus := range orderStatusMap {
		orderList = append(orderList, &inventory.OrderStorageView{
			OrderNumber:   orderNumber,
			StorageStatus: uint32(storageStatus),
		})
	}
	resp = &inventory.GetOrderListStorageResponse{
		OrderList: orderList,
	}
	return
}

// GetWarehouseStorage 查看仓库实时库存
func (s *Service) GetWarehouseStorage(ctx context.Context, req *inventory.GetWarehouseStorageRequest) (
	resp *inventory.GetWarehouseStorageResponse, err error) {
	empId, sgId := req.WebToken.EmpId, req.WebToken.SgId
	if _, err = s.CheckUsagePermission(ctx, empId, req.PStoreId, req.Id, constant.VisitWarehouseData); err != nil {
		return
	}
	var (
		storages        []*model.Storage
		total           int64
		objectKeys      []string
		materialRemarks map[string]*model.FullRemark
		priceMap        map[string]uint64
	)
	if total, storages, err = s.dao.FindWarehouseStorage(ctx, sgId, req.Id, int(req.PageNo), int(req.PageSize),
		int(req.QueryType), req.QueryTypeId, req.QueryKeyword, req.QueryStorageKeys); err != nil {
		return
	}
	materialIDMap := make(util.UInt64BoolMap)
	storageKeyMap := make(util.StringBoolMap)
	for _, storage := range storages {
		storageKeyMap[storage.StorageKey] = true
		switch inventory.StorageType(storage.StorageType) {
		case inventory.StorageType_MATERIAL:
			materialIDMap[*storage.MaterialID] = true
		case inventory.StorageType_PROD:
			objectKeys = append(objectKeys, storage.StorageKey)
		default:
			err = constant.ErrObjectNotExist
			return
		}
	}
	remoteTypeM := make(map[string]*prod.SelectionObjectView)
	if len(objectKeys) > 0 {
		var objects []*prod.SelectionObjectView
		if objects, err = s.downstream.FetchObjects(ctx, sgId, objectKeys); err != nil {
			return
		}
		for _, o := range objects {
			remoteTypeM[o.Id] = o
		}
	}
	if len(materialIDMap) > 0 {
		if materialRemarks, err = s.dao.BuildMaterialsRemarks(ctx, sgId, materialIDMap.GetKeys()); err != nil {
			return
		}
		if len(materialRemarks) != len(materialIDMap) {
			err = constant.ErrSystemError
			return
		}
	}
	// 2. 加载上个月期末金额
	if len(storageKeyMap) != 0 {
		if priceMap, err = s.dao.GetLastMonthStoragePriceByStorageKeys(
			ctx, req.Id, storageKeyMap.GetKeys(), time.Now(), materialRemarks, constant.UnitTypeStorage); err != nil {
			return
		}
	}
	views := make([]*inventory.WarehouseStorageView, len(storages))
	for i, storage := range storages {
		r := model.StorageRealTimeRemark{}
		if err = json.Unmarshal(storage.Remark, &r); err != nil {
			return
		}
		storageType := inventory.StorageType(storage.StorageType)
		if storageType == inventory.StorageType_PROD {
			r.PurchaseRatio = 1
			r.PurchaseUnit = r.Unit
		}
		price, _ := priceMap[storage.StorageKey]
		views[i] = &inventory.WarehouseStorageView{
			StorageType:     storageType,
			StorageKey:      storage.StorageKey,
			Name:            r.Name,
			SkuName:         r.SkuName,
			Price:           price,
			CurrentQuantity: storage.Quantity,
			FreezeQuantity:  storage.FreezeQuantity,
			Code:            r.Code,
			Unit:            r.Unit,
			StorageUnit:     r.StorageUnit,
			StorageRatio:    r.StorageRatio,
			PurchaseUnit:    r.PurchaseUnit,
			PurchaseRatio:   r.StorageRatio,
		}
		if inventory.StorageType(storage.StorageType) != inventory.StorageType_MATERIAL {
			views[i].Price = uint64(remoteTypeM[storage.StorageKey].Price)
			views[i].TypeName = remoteTypeM[storage.StorageKey].TypeName
			views[i].PStoreId = remoteTypeM[storage.StorageKey].PStoreId
		} else {
			// 本地数据，不展示快照，强制覆盖
			remark := materialRemarks[storage.StorageKey]
			views[i].TypeName = remark.Type
			views[i].Name = remark.Name
			views[i].Code = remark.Code
			views[i].PurchaseRatio = remark.PurchaseRatio
			views[i].PurchaseUnit = remark.PurchaseUnit
			views[i].StorageRatio = remark.StorageRatio
			views[i].StorageUnit = remark.StorageUnit
			views[i].PStoreId = remark.PStoreID
		}
	}
	resp = &inventory.GetWarehouseStorageResponse{
		Total: uint32(total),
		Data:  views,
	}
	return
}
